﻿///////////////////////////////////////////////////////////////////////////////
/// Copyright (c) by longshuang@msn.cn 2014.
/// All rights reserved.
///
/// @File	: Engine.File.hpp
/// @Brief	: 文件解析相关接口
/// @Author	: Leo Zhao <longshuang@msn.cn>
/// @Date	: 2014-12-23
///
/// CHANGELOG
///		2015-09-03	修改遍历INI方式
/// 	2014-12-23	初版提交
///////////////////////////////////////////////////////////////////////////////
#ifndef		__ENGINE_FILE_INCLUDE__
#define		__ENGINE_FILE_INCLUDE__

#include	<string>
#include	<map>
#include	<vector>

namespace Engine { namespace File {
	
	/// @Brief	: INI类型文件解析
	/// @Note	: 
	///		1. 使用引号后Session，Key，Value中可以使用其他任意字符，但不得为空
	///		2. 注释符为'#'或';'。注释符至行尾被忽略
	/// 
	///		["Session A"]				# right
	///		"Key 1" = "Value = [2]"		# right
	///		Key_2 = 1					# right
	///
	///		[SessionB]					# right. No Keys
	///		
	///		[Session B]					# error ErrHasSpaceInSession
	///		Key							# error ErrEqualMismatch
	/// @Usage	: 这里只说明一下遍历Ini的方法
	///		Engine::File::IniFile iIni;
	///		if (iIni.Load("XXX.ini") != Engine::File::IniFile::Success) { ... }
	///		for (auto itSession = iIni.Iterator(); itSession; ++itSession) {
	///			for (auto itKey = itSession.Iterator(); itKey; ++itKey) {
	///				XXX value = iIni.GetXXX(itSession.Session(), itKey.Key(), xxx);
	///			}
	///		}
	class IniFile {
	public:
		enum {
			Success		= 0,		//! 正确
			ErrNothing	= 0,		//! 无错误
			ErrBadParam,			//! 参数错误
			ErrFileAccess,			//! 文件无法读取
			ErrLostSession,			//! 在定义Session之前出现Key
			ErrLostKey,				//! 在定义Key之前出现Value
			ErrSessionSyntax,		//! 在Session结束后出现字符且不是注释符号 或 未使用引号的Session中间出现'[= "'等
			ErrKeySyntax,			//! Key结束后不是等号 或 未使用引号的Key中间出现"[]= ""等
			ErrValueSyntax,			//! 未使用引号的Value中间出现"[]= ""等
			ErrBraceMismatch,		//! 大括号不匹配
			ErrQuoteMismatch,		//! 引号不匹配
			ErrEqualMismatch,		//! 出现空Key,且后面没有等号（允许空key加一个等号）
			ErrLineSize,			//!	行超过1024个字节限制
		};

		typedef std::map<std::string, std::string>	HashMapValue;
		typedef std::map<std::string, HashMapValue>	HashMapContent;

		class KeyIterator {
		public:
			KeyIterator(HashMapValue & rMap) : _iItor(rMap.begin()), _iEnd(rMap.end()) {}
			virtual ~KeyIterator() {}

			inline				operator bool() const { return _iItor != _iEnd; }
			inline void			operator++() { _iItor++; }
			inline const char *	Key() { return _iItor->first.c_str(); }

		private:
			HashMapValue::iterator	_iItor;
			HashMapValue::iterator	_iEnd;
		};
		
		class SessionIterator {
		public:
			SessionIterator(HashMapContent & rMap) : _iItor(rMap.begin()), _iEnd(rMap.end()) {}
			virtual ~SessionIterator() {}

			inline				operator bool() const { return _iItor != _iEnd; }
			inline void			operator++() { _iItor++; }
			inline const char *	Session() { return _iItor->first.c_str(); }
			inline KeyIterator	Iterator() { return KeyIterator(_iItor->second); 	}

		private:
			HashMapContent::iterator	_iItor;
			HashMapContent::iterator	_iEnd;
		};

	public:
		IniFile() {}
		virtual ~IniFile() {}

		/// @Brief	: 文件加载与保存
		/// @Param	: szFile	文件路径
		/// @Return	: 操作是否成功
		int			Load(const char * szFile);
		int			Save(const char * szFile);

		/// @Brief	: 读取INI文件中配置的值
		/// @Param	: szSession	INI中方括号中的内容
		/// @Param	: szKey		INI中等号之前的内容
		/// @Param	: XDefault	如果读取失败时返回的默认值
		/// @Return	: 返回INI文件中配置的值
		int			GetInt(const char * szSession, const char * szKey, int nDefault);
		double		GetDouble(const char * szSession, const char * szKey, double dDefault);
		bool		GetBool(const char * szSession, const char * szKey, bool bDefault);
		std::string	GetString(const char * szSession, const char * szKey, const std::string & szDefault);

		/// @Brief	: 设置INI文件中的配置
		/// @Param	: szSession	INI中方括号中的内容
		/// @Param	: szKey		INI中等号之前的内容
		/// @Param	: XValue	新的值
		/// @Return	: NONE
		void		SetInt(const char * szSession, const char * szKey, int nValue);
		void		SetDouble(const char * szSession, const char * szKey, double dValue);
		void		SetBool(const char * szSession, const char * szKey, bool bValue);
		void		SetString(const char * szSession, const char * szKey, const std::string & szValue);

		/// @Brief	: Session遍历器
		/// @Param	: NONE
		/// @Return	: 返回Session遍历器
		inline SessionIterator	Iterator() { return SessionIterator(_iMapContent); }

	private:
		HashMapContent	_iMapContent;
	};

	/// @Brief	: CSV类型文件解析，仅支持简单CSV格式（不解析引号作用）
	class CsvFile {
	public:
		typedef std::vector<std::string>		StringVector;
		typedef std::vector<StringVector>		StringTable;
		typedef enum { Tab = 0,	Comma }			SplitType;

	public:
		CsvFile() {}
		virtual ~CsvFile() {}

		/// @Brief	: 文件加载与保存
		/// @Param	: szFile	文件路径
		/// @Param	: emType	内容是以何种方式分隔
		/// @Return	: 操作是否成功
		bool		Load(const char * szFile, SplitType emType = SplitType::Tab);
		bool		Save(const char * szFile, SplitType emType = SplitType::Tab);

		/// @Brief	: 获取文件有效行数
		/// @Param	: NONE
		/// @Return	: 返回文件有效的行数
		size_t		GetLineCount() { return _vContent.size(); }

		/// @Brief	: 获取文件有效列数
		/// @Param	: NONE
		/// @Return	: 返回所有有效的Title数
		size_t		GetColumnCount() { return _vKeys.size(); }

		/// @Brief	: 获取当前列的Title
		/// @Param	: nIdx		从0开始的列序号
		/// @Return	: 返回该列的Title
		std::string	GetColumnName(int nIdx);

		/// @Brief	: 读取Csv文件中配置的值
		/// @Param	: nLine		获取的行号，从0开始
		/// @Param	: szCol		该行对应的键值
		/// @Param	: XDefault	如果读取失败时返回的默认值
		/// @Return	: 返回Csv文件中配置的值
		int			GetInt(size_t nLine, const char * szCol, int nDefault);
		double		GetDouble(size_t nLine, const char * szCol, double dDefault);
		bool		GetBool(size_t nLine, const char * szCol, bool bDefault);
		std::string	GetString(size_t nLine, const char * szCol, const std::string & szDefault);

		/// @Brief	: 设置Csv文件中的配置
		/// @Param	: nLine		CSV行数，nLine > GetLineCount()时操作无效
		/// @Param	: szCol		该行对应的键值
		/// @Param	: XValue	新的值
		/// @Return	: NONE
		void		SetInt(size_t nLine, const char * szCol, int nValue);
		void		SetDouble(size_t nLine, const char * szCol, double dValue);
		void		SetBool(size_t nLine, const char * szCol, bool bValue);
		void		SetString(size_t nLine, const char * szCol, const std::string & szValue);

		/// @Brief	: 在最后添加一行空行
		/// @Param	: NONE
		/// @Return	: 返回该新行的行序号
		size_t		AddLine();

	private:
		StringVector	_vKeys;
		StringTable		_vContent;

	};

}}

#endif//!	__ENGINE_FILE_INCLUDE__
